/**
 * tracking - A modern approach for Computer Vision on the web.
 * @author Eduardo Lundgren <edu@rdo.io>
 * @version v1.1.3
 * @link http://trackingjs.com
 * @license BSD
 */
/* eslint-disable */
;(function (window, undefined) {
  window.tracking = window.tracking || {}

  /**
   * Inherit the prototype methods from one constructor into another.
   *
   * Usage:
   * <pre>
   * function ParentClass(a, b) { }
   * ParentClass.prototype.foo = function(a) { }
   *
   * function ChildClass(a, b, c) {
   *   tracking.base(this, a, b);
   * }
   * tracking.inherits(ChildClass, ParentClass);
   *
   * var child = new ChildClass('a', 'b', 'c');
   * child.foo();
   * </pre>
   *
   * @param {Function} childCtor Child class.
   * @param {Function} parentCtor Parent class.
   */
  tracking.inherits = function (childCtor, parentCtor) {
    function TempCtor() {}
    TempCtor.prototype = parentCtor.prototype
    childCtor.superClass_ = parentCtor.prototype
    childCtor.prototype = new TempCtor()
    childCtor.prototype.constructor = childCtor

    /**
     * Calls superclass constructor/method.
     *
     * This function is only available if you use tracking.inherits to express
     * inheritance relationships between classes.
     *
     * @param {!object} me Should always be "this".
     * @param {string} methodName The method name to call. Calling superclass
     *     constructor can be done with the special string 'constructor'.
     * @param {...*} var_args The arguments to pass to superclass
     *     method/constructor.
     * @return {*} The return value of the superclass method/constructor.
     */
    childCtor.base = function (me, methodName) {
      var args = Array.prototype.slice.call(arguments, 2)
      return parentCtor.prototype[methodName].apply(me, args)
    }
  }

  /**
   * Captures the user camera when tracking a video element and set its source
   * to the camera stream.
   * @param {HTMLVideoElement} element Canvas element to track.
   * @param {object} opt_options Optional configuration to the tracker.
   */
  tracking.initUserMedia_ = function (element, opt_options) {
    window.navigator.mediaDevices
      .getUserMedia(opt_options)
      .then(function (stream) {
        element.srcObject = stream
      })
      .catch(function (err) {
        throw Error('Cannot capture user camera.')
      })
  }

  /**
   * Tests whether the object is a dom node.
   * @param {object} o Object to be tested.
   * @return {boolean} True if the object is a dom node.
   */
  tracking.isNode = function (o) {
    return o.nodeType || this.isWindow(o)
  }

  /**
   * Tests whether the object is the `window` object.
   * @param {object} o Object to be tested.
   * @return {boolean} True if the object is the `window` object.
   */
  tracking.isWindow = function (o) {
    return !!(o && o.alert && o.document)
  }

  /**
   * Selects a dom node from a CSS3 selector using `document.querySelector`.
   * @param {string} selector
   * @param {object} opt_element The root element for the query. When not
   *     specified `document` is used as root element.
   * @return {HTMLElement} The first dom element that matches to the selector.
   *     If not found, returns `null`.
   */
  tracking.one = function (selector, opt_element) {
    if (this.isNode(selector)) {
      return selector
    }
    return (opt_element || document).querySelector(selector)
  }

  /**
   * Tracks a canvas, image or video element based on the specified `tracker`
   * instance. This method extract the pixel information of the input element
   * to pass to the `tracker` instance. When tracking a video, the
   * `tracker.track(pixels, width, height)` will be in a
   * `requestAnimationFrame` loop in order to track all video frames.
   *
   * Example:
   * var tracker = new tracking.ColorTracker();
   *
   * tracking.track('#video', tracker);
   * or
   * tracking.track('#video', tracker, { camera: true });
   *
   * tracker.on('track', function(event) {
   *   // console.log(event.data[0].x, event.data[0].y)
   * });
   *
   * @param {HTMLElement} element The element to track, canvas, image or
   *     video.
   * @param {tracking.Tracker} tracker The tracker instance used to track the
   *     element.
   * @param {object} opt_options Optional configuration to the tracker.
   */
  tracking.track = function (element, tracker, opt_options) {
    element = tracking.one(element)
    if (!element) {
      throw new Error('Element not found, try a different element or selector.')
    }
    if (!tracker) {
      throw new Error(
        'Tracker not specified, try `tracking.track(element, new tracking.FaceTracker())`.'
      )
    }

    switch (element.nodeName.toLowerCase()) {
      case 'canvas':
        return this.trackCanvas_(element, tracker, opt_options)
      case 'img':
        return this.trackImg_(element, tracker, opt_options)
      case 'video':
        if (opt_options) {
          this.initUserMedia_(element, opt_options)
        }
        return this.trackVideo_(element, tracker, opt_options)
      default:
        throw new Error(
          'Element not supported, try in a canvas, img, or video.'
        )
    }
  }

  /**
   * Tracks a canvas element based on the specified `tracker` instance and
   * returns a `TrackerTask` for this track.
   * @param {HTMLCanvasElement} element Canvas element to track.
   * @param {tracking.Tracker} tracker The tracker instance used to track the
   *     element.
   * @param {object} opt_options Optional configuration to the tracker.
   * @return {tracking.TrackerTask}
   * @private
   */
  tracking.trackCanvas_ = function (element, tracker) {
    var self = this
    var task = new tracking.TrackerTask(tracker)
    task.on('run', function () {
      self.trackCanvasInternal_(element, tracker)
    })
    return task.run()
  }

  /**
   * Tracks a canvas element based on the specified `tracker` instance. This
   * method extract the pixel information of the input element to pass to the
   * `tracker` instance.
   * @param {HTMLCanvasElement} element Canvas element to track.
   * @param {tracking.Tracker} tracker The tracker instance used to track the
   *     element.
   * @param {object} opt_options Optional configuration to the tracker.
   * @private
   */
  tracking.trackCanvasInternal_ = function (element, tracker) {
    var width = element.width
    var height = element.height
    var context = element.getContext('2d')
    var imageData = context.getImageData(0, 0, width, height)
    tracker.track(imageData.data, width, height)
  }

  /**
   * Tracks a image element based on the specified `tracker` instance. This
   * method extract the pixel information of the input element to pass to the
   * `tracker` instance.
   * @param {HTMLImageElement} element Canvas element to track.
   * @param {tracking.Tracker} tracker The tracker instance used to track the
   *     element.
   * @param {object} opt_options Optional configuration to the tracker.
   * @private
   */
  tracking.trackImg_ = function (element, tracker) {
    var width = element.width
    var height = element.height
    var canvas = document.createElement('canvas')

    canvas.width = width
    canvas.height = height

    var task = new tracking.TrackerTask(tracker)
    task.on('run', function () {
      tracking.Canvas.loadImage(
        canvas,
        element.src,
        0,
        0,
        width,
        height,
        function () {
          tracking.trackCanvasInternal_(canvas, tracker)
        }
      )
    })
    return task.run()
  }

  /**
   * Tracks a video element based on the specified `tracker` instance. This
   * method extract the pixel information of the input element to pass to the
   * `tracker` instance. The `tracker.track(pixels, width, height)` will be in
   * a `requestAnimationFrame` loop in order to track all video frames.
   * @param {HTMLVideoElement} element Canvas element to track.
   * @param {tracking.Tracker} tracker The tracker instance used to track the
   *     element.
   * @param {object} opt_options Optional configuration to the tracker.
   * @private
   */
  tracking.trackVideo_ = function (element, tracker) {
    var canvas = document.createElement('canvas')
    var context = canvas.getContext('2d')
    var width
    var height

    var resizeCanvas_ = function () {
      width = element.offsetWidth
      height = element.offsetHeight
      canvas.width = width
      canvas.height = height
    }
    resizeCanvas_()
    element.addEventListener('resize', resizeCanvas_)

    var requestId
    var requestAnimationFrame_ = function () {
      requestId = window.requestAnimationFrame(function () {
        if (element.readyState === element.HAVE_ENOUGH_DATA) {
          try {
            // Firefox v~30.0 gets confused with the video readyState firing an
            // erroneous HAVE_ENOUGH_DATA just before HAVE_CURRENT_DATA state,
            // hence keep trying to read it until resolved.
            context.drawImage(element, 0, 0, width, height)
          } catch (err) {}
          tracking.trackCanvasInternal_(canvas, tracker)
        }
        requestAnimationFrame_()
      })
    }

    var task = new tracking.TrackerTask(tracker)
    task.on('stop', function () {
      window.cancelAnimationFrame(requestId)
    })
    task.on('run', function () {
      requestAnimationFrame_()
    })
    return task.run()
  }

  // Browser polyfills
  //===================

  if (!window.URL) {
    window.URL = window.URL || window.webkitURL || window.msURL || window.oURL
  }

  if (!navigator.getUserMedia) {
    navigator.getUserMedia =
      navigator.getUserMedia ||
      navigator.webkitGetUserMedia ||
      navigator.mozGetUserMedia ||
      navigator.msGetUserMedia
  }
})(window)
;(function () {
  /**
   * EventEmitter utility.
   * @constructor
   */
  tracking.EventEmitter = function () {}

  /**
   * Holds event listeners scoped by event type.
   * @type {object}
   * @private
   */
  tracking.EventEmitter.prototype.events_ = null

  /**
   * Adds a listener to the end of the listeners array for the specified event.
   * @param {string} event
   * @param {function} listener
   * @return {object} Returns emitter, so calls can be chained.
   */
  tracking.EventEmitter.prototype.addListener = function (event, listener) {
    if (typeof listener !== 'function') {
      throw new TypeError('Listener must be a function')
    }
    if (!this.events_) {
      this.events_ = {}
    }

    this.emit('newListener', event, listener)

    if (!this.events_[event]) {
      this.events_[event] = []
    }

    this.events_[event].push(listener)

    return this
  }

  /**
   * Returns an array of listeners for the specified event.
   * @param {string} event
   * @return {array} Array of listeners.
   */
  tracking.EventEmitter.prototype.listeners = function (event) {
    return this.events_ && this.events_[event]
  }

  /**
   * Execute each of the listeners in order with the supplied arguments.
   * @param {string} event
   * @param {*} opt_args [arg1], [arg2], [...]
   * @return {boolean} Returns true if event had listeners, false otherwise.
   */
  tracking.EventEmitter.prototype.emit = function (event) {
    var listeners = this.listeners(event)
    if (listeners) {
      var args = Array.prototype.slice.call(arguments, 1)
      for (var i = 0; i < listeners.length; i++) {
        if (listeners[i]) {
          listeners[i].apply(this, args)
        }
      }
      return true
    }
    return false
  }

  /**
   * Adds a listener to the end of the listeners array for the specified event.
   * @param {string} event
   * @param {function} listener
   * @return {object} Returns emitter, so calls can be chained.
   */
  tracking.EventEmitter.prototype.on =
    tracking.EventEmitter.prototype.addListener

  /**
   * Adds a one time listener for the event. This listener is invoked only the
   * next time the event is fired, after which it is removed.
   * @param {string} event
   * @param {function} listener
   * @return {object} Returns emitter, so calls can be chained.
   */
  tracking.EventEmitter.prototype.once = function (event, listener) {
    var self = this
    self.on(event, function handlerInternal() {
      self.removeListener(event, handlerInternal)
      listener.apply(this, arguments)
    })
  }

  /**
   * Removes all listeners, or those of the specified event. It's not a good
   * idea to remove listeners that were added elsewhere in the code,
   * especially when it's on an emitter that you didn't create.
   * @param {string} event
   * @return {object} Returns emitter, so calls can be chained.
   */
  tracking.EventEmitter.prototype.removeAllListeners = function (opt_event) {
    if (!this.events_) {
      return this
    }
    if (opt_event) {
      delete this.events_[opt_event]
    } else {
      delete this.events_
    }
    return this
  }

  /**
   * Remove a listener from the listener array for the specified event.
   * Caution: changes array indices in the listener array behind the listener.
   * @param {string} event
   * @param {function} listener
   * @return {object} Returns emitter, so calls can be chained.
   */
  tracking.EventEmitter.prototype.removeListener = function (event, listener) {
    if (typeof listener !== 'function') {
      throw new TypeError('Listener must be a function')
    }
    if (!this.events_) {
      return this
    }

    var listeners = this.listeners(event)
    if (Array.isArray(listeners)) {
      var i = listeners.indexOf(listener)
      if (i < 0) {
        return this
      }
      listeners.splice(i, 1)
    }

    return this
  }

  /**
   * By default EventEmitters will print a warning if more than 10 listeners
   * are added for a particular event. This is a useful default which helps
   * finding memory leaks. Obviously not all Emitters should be limited to 10.
   * This function allows that to be increased. Set to zero for unlimited.
   * @param {number} n The maximum number of listeners.
   */
  tracking.EventEmitter.prototype.setMaxListeners = function () {
    throw new Error('Not implemented')
  }
})()
;(function () {
  /**
   * Canvas utility.
   * @static
   * @constructor
   */
  tracking.Canvas = {}

  /**
   * Loads an image source into the canvas.
   * @param {HTMLCanvasElement} canvas The canvas dom element.
   * @param {string} src The image source.
   * @param {number} x The canvas horizontal coordinate to load the image.
   * @param {number} y The canvas vertical coordinate to load the image.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {function} opt_callback Callback that fires when the image is loaded
   *     into the canvas.
   * @static
   */
  tracking.Canvas.loadImage = function (
    canvas,
    src,
    x,
    y,
    width,
    height,
    opt_callback
  ) {
    var instance = this
    var img = new window.Image()
    img.crossOrigin = '*'
    img.onload = function () {
      var context = canvas.getContext('2d')
      canvas.width = width
      canvas.height = height
      context.drawImage(img, x, y, width, height)
      if (opt_callback) {
        opt_callback.call(instance)
      }
      img = null
    }
    img.src = src
  }
})()
;(function () {
  /**
   * DisjointSet utility with path compression. Some applications involve
   * grouping n distinct objects into a collection of disjoint sets. Two
   * important operations are then finding which set a given object belongs to
   * and uniting the two sets. A disjoint set data structure maintains a
   * collection S={ S1 , S2 ,..., Sk } of disjoint dynamic sets. Each set is
   * identified by a representative, which usually is a member in the set.
   * @static
   * @constructor
   */
  tracking.DisjointSet = function (length) {
    if (length === undefined) {
      throw new Error('DisjointSet length not specified.')
    }
    this.length = length
    this.parent = new Uint32Array(length)
    for (var i = 0; i < length; i++) {
      this.parent[i] = i
    }
  }

  /**
   * Holds the length of the internal set.
   * @type {number}
   */
  tracking.DisjointSet.prototype.length = null

  /**
   * Holds the set containing the representative values.
   * @type {Array.<number>}
   */
  tracking.DisjointSet.prototype.parent = null

  /**
   * Finds a pointer to the representative of the set containing i.
   * @param {number} i
   * @return {number} The representative set of i.
   */
  tracking.DisjointSet.prototype.find = function (i) {
    if (this.parent[i] === i) {
      return i
    } else {
      return (this.parent[i] = this.find(this.parent[i]))
    }
  }

  /**
   * Unites two dynamic sets containing objects i and j, say Si and Sj, into
   * a new set that Si ∪ Sj, assuming that Si ∩ Sj = ∅;
   * @param {number} i
   * @param {number} j
   */
  tracking.DisjointSet.prototype.union = function (i, j) {
    var iRepresentative = this.find(i)
    var jRepresentative = this.find(j)
    this.parent[iRepresentative] = jRepresentative
  }
})()
;(function () {
  /**
   * Image utility.
   * @static
   * @constructor
   */
  tracking.Image = {}

  /**
   * Computes gaussian blur. Adapted from
   * https://github.com/kig/canvasfilters.
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {number} diameter Gaussian blur diameter, must be greater than 1.
   * @return {array} The edge pixels in a linear [r,g,b,a,...] array.
   */
  tracking.Image.blur = function (pixels, width, height, diameter) {
    diameter = Math.abs(diameter)
    if (diameter <= 1) {
      throw new Error('Diameter should be greater than 1.')
    }
    var radius = diameter / 2
    var len = Math.ceil(diameter) + (1 - (Math.ceil(diameter) % 2))
    var weights = new Float32Array(len)
    var rho = (radius + 0.5) / 3
    var rhoSq = rho * rho
    var gaussianFactor = 1 / Math.sqrt(2 * Math.PI * rhoSq)
    var rhoFactor = -1 / (2 * rho * rho)
    var wsum = 0
    var middle = Math.floor(len / 2)
    for (var i = 0; i < len; i++) {
      var x = i - middle
      var gx = gaussianFactor * Math.exp(x * x * rhoFactor)
      weights[i] = gx
      wsum += gx
    }
    for (var j = 0; j < weights.length; j++) {
      weights[j] /= wsum
    }
    return this.separableConvolve(
      pixels,
      width,
      height,
      weights,
      weights,
      false
    )
  }

  /**
   * Computes the integral image for summed, squared, rotated and sobel pixels.
   * @param {array} pixels The pixels in a linear [r,g,b,a,...] array to loop
   *     through.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {array} opt_integralImage Empty array of size `width * height` to
   *     be filled with the integral image values. If not specified compute sum
   *     values will be skipped.
   * @param {array} opt_integralImageSquare Empty array of size `width *
   *     height` to be filled with the integral image squared values. If not
   *     specified compute squared values will be skipped.
   * @param {array} opt_tiltedIntegralImage Empty array of size `width *
   *     height` to be filled with the rotated integral image values. If not
   *     specified compute sum values will be skipped.
   * @param {array} opt_integralImageSobel Empty array of size `width *
   *     height` to be filled with the integral image of sobel values. If not
   *     specified compute sobel filtering will be skipped.
   * @static
   */
  tracking.Image.computeIntegralImage = function (
    pixels,
    width,
    height,
    opt_integralImage,
    opt_integralImageSquare,
    opt_tiltedIntegralImage,
    opt_integralImageSobel
  ) {
    if (arguments.length < 4) {
      throw new Error(
        'You should specify at least one output array in the order: sum, square, tilted, sobel.'
      )
    }
    var pixelsSobel
    if (opt_integralImageSobel) {
      pixelsSobel = tracking.Image.sobel(pixels, width, height)
    }
    for (var i = 0; i < height; i++) {
      for (var j = 0; j < width; j++) {
        var w = i * width * 4 + j * 4
        var pixel = ~~(
          pixels[w] * 0.299 +
          pixels[w + 1] * 0.587 +
          pixels[w + 2] * 0.114
        )
        if (opt_integralImage) {
          this.computePixelValueSAT_(opt_integralImage, width, i, j, pixel)
        }
        if (opt_integralImageSquare) {
          this.computePixelValueSAT_(
            opt_integralImageSquare,
            width,
            i,
            j,
            pixel * pixel
          )
        }
        if (opt_tiltedIntegralImage) {
          var w1 = w - width * 4
          var pixelAbove = ~~(
            pixels[w1] * 0.299 +
            pixels[w1 + 1] * 0.587 +
            pixels[w1 + 2] * 0.114
          )
          this.computePixelValueRSAT_(
            opt_tiltedIntegralImage,
            width,
            i,
            j,
            pixel,
            pixelAbove || 0
          )
        }
        if (opt_integralImageSobel) {
          this.computePixelValueSAT_(
            opt_integralImageSobel,
            width,
            i,
            j,
            pixelsSobel[w]
          )
        }
      }
    }
  }

  /**
   * Helper method to compute the rotated summed area table (RSAT) by the
   * formula:
   *
   * RSAT(x, y) = RSAT(x-1, y-1) + RSAT(x+1, y-1) - RSAT(x, y-2) + I(x, y) + I(x, y-1)
   *
   * @param {number} width The image width.
   * @param {array} RSAT Empty array of size `width * height` to be filled with
   *     the integral image values. If not specified compute sum values will be
   *     skipped.
   * @param {number} i Vertical position of the pixel to be evaluated.
   * @param {number} j Horizontal position of the pixel to be evaluated.
   * @param {number} pixel Pixel value to be added to the integral image.
   * @static
   * @private
   */
  tracking.Image.computePixelValueRSAT_ = function (
    RSAT,
    width,
    i,
    j,
    pixel,
    pixelAbove
  ) {
    var w = i * width + j
    RSAT[w] =
      (RSAT[w - width - 1] || 0) +
      (RSAT[w - width + 1] || 0) -
      (RSAT[w - width - width] || 0) +
      pixel +
      pixelAbove
  }

  /**
   * Helper method to compute the summed area table (SAT) by the formula:
   *
   * SAT(x, y) = SAT(x, y-1) + SAT(x-1, y) + I(x, y) - SAT(x-1, y-1)
   *
   * @param {number} width The image width.
   * @param {array} SAT Empty array of size `width * height` to be filled with
   *     the integral image values. If not specified compute sum values will be
   *     skipped.
   * @param {number} i Vertical position of the pixel to be evaluated.
   * @param {number} j Horizontal position of the pixel to be evaluated.
   * @param {number} pixel Pixel value to be added to the integral image.
   * @static
   * @private
   */
  tracking.Image.computePixelValueSAT_ = function (SAT, width, i, j, pixel) {
    var w = i * width + j
    SAT[w] =
      (SAT[w - width] || 0) +
      (SAT[w - 1] || 0) +
      pixel -
      (SAT[w - width - 1] || 0)
  }

  /**
   * Converts a color from a colorspace based on an RGB color model to a
   * grayscale representation of its luminance. The coefficients represent the
   * measured intensity perception of typical trichromat humans, in
   * particular, human vision is most sensitive to green and least sensitive
   * to blue.
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {boolean} fillRGBA If the result should fill all RGBA values with the gray scale
   *  values, instead of returning a single value per pixel.
   * @param {Uint8ClampedArray} The grayscale pixels in a linear array ([p,p,p,a,...] if fillRGBA
   *  is true and [p1, p2, p3, ...] if fillRGBA is false).
   * @static
   */
  tracking.Image.grayscale = function (pixels, width, height, fillRGBA) {
    var gray = new Uint8ClampedArray(
      fillRGBA ? pixels.length : pixels.length >> 2
    )
    var p = 0
    var w = 0
    for (var i = 0; i < height; i++) {
      for (var j = 0; j < width; j++) {
        var value =
          pixels[w] * 0.299 + pixels[w + 1] * 0.587 + pixels[w + 2] * 0.114
        gray[p++] = value

        if (fillRGBA) {
          gray[p++] = value
          gray[p++] = value
          gray[p++] = pixels[w + 3]
        }

        w += 4
      }
    }
    return gray
  }

  /**
   * Fast horizontal separable convolution. A point spread function (PSF) is
   * said to be separable if it can be broken into two one-dimensional
   * signals: a vertical and a horizontal projection. The convolution is
   * performed by sliding the kernel over the image, generally starting at the
   * top left corner, so as to move the kernel through all the positions where
   * the kernel fits entirely within the boundaries of the image. Adapted from
   * https://github.com/kig/canvasfilters.
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {array} weightsVector The weighting vector, e.g [-1,0,1].
   * @param {number} opaque
   * @return {array} The convoluted pixels in a linear [r,g,b,a,...] array.
   */
  tracking.Image.horizontalConvolve = function (
    pixels,
    width,
    height,
    weightsVector,
    opaque
  ) {
    var side = weightsVector.length
    var halfSide = Math.floor(side / 2)
    var output = new Float32Array(width * height * 4)
    var alphaFac = opaque ? 1 : 0

    for (var y = 0; y < height; y++) {
      for (var x = 0; x < width; x++) {
        var sy = y
        var sx = x
        var offset = (y * width + x) * 4
        var r = 0
        var g = 0
        var b = 0
        var a = 0
        for (var cx = 0; cx < side; cx++) {
          var scy = sy
          var scx = Math.min(width - 1, Math.max(0, sx + cx - halfSide))
          var poffset = (scy * width + scx) * 4
          var wt = weightsVector[cx]
          r += pixels[poffset] * wt
          g += pixels[poffset + 1] * wt
          b += pixels[poffset + 2] * wt
          a += pixels[poffset + 3] * wt
        }
        output[offset] = r
        output[offset + 1] = g
        output[offset + 2] = b
        output[offset + 3] = a + alphaFac * (255 - a)
      }
    }
    return output
  }

  /**
   * Fast vertical separable convolution. A point spread function (PSF) is
   * said to be separable if it can be broken into two one-dimensional
   * signals: a vertical and a horizontal projection. The convolution is
   * performed by sliding the kernel over the image, generally starting at the
   * top left corner, so as to move the kernel through all the positions where
   * the kernel fits entirely within the boundaries of the image. Adapted from
   * https://github.com/kig/canvasfilters.
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {array} weightsVector The weighting vector, e.g [-1,0,1].
   * @param {number} opaque
   * @return {array} The convoluted pixels in a linear [r,g,b,a,...] array.
   */
  tracking.Image.verticalConvolve = function (
    pixels,
    width,
    height,
    weightsVector,
    opaque
  ) {
    var side = weightsVector.length
    var halfSide = Math.floor(side / 2)
    var output = new Float32Array(width * height * 4)
    var alphaFac = opaque ? 1 : 0

    for (var y = 0; y < height; y++) {
      for (var x = 0; x < width; x++) {
        var sy = y
        var sx = x
        var offset = (y * width + x) * 4
        var r = 0
        var g = 0
        var b = 0
        var a = 0
        for (var cy = 0; cy < side; cy++) {
          var scy = Math.min(height - 1, Math.max(0, sy + cy - halfSide))
          var scx = sx
          var poffset = (scy * width + scx) * 4
          var wt = weightsVector[cy]
          r += pixels[poffset] * wt
          g += pixels[poffset + 1] * wt
          b += pixels[poffset + 2] * wt
          a += pixels[poffset + 3] * wt
        }
        output[offset] = r
        output[offset + 1] = g
        output[offset + 2] = b
        output[offset + 3] = a + alphaFac * (255 - a)
      }
    }
    return output
  }

  /**
   * Fast separable convolution. A point spread function (PSF) is said to be
   * separable if it can be broken into two one-dimensional signals: a
   * vertical and a horizontal projection. The convolution is performed by
   * sliding the kernel over the image, generally starting at the top left
   * corner, so as to move the kernel through all the positions where the
   * kernel fits entirely within the boundaries of the image. Adapted from
   * https://github.com/kig/canvasfilters.
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {array} horizWeights The horizontal weighting vector, e.g [-1,0,1].
   * @param {array} vertWeights The vertical vector, e.g [-1,0,1].
   * @param {number} opaque
   * @return {array} The convoluted pixels in a linear [r,g,b,a,...] array.
   */
  tracking.Image.separableConvolve = function (
    pixels,
    width,
    height,
    horizWeights,
    vertWeights,
    opaque
  ) {
    var vertical = this.verticalConvolve(
      pixels,
      width,
      height,
      vertWeights,
      opaque
    )
    return this.horizontalConvolve(
      vertical,
      width,
      height,
      horizWeights,
      opaque
    )
  }

  /**
   * Compute image edges using Sobel operator. Computes the vertical and
   * horizontal gradients of the image and combines the computed images to
   * find edges in the image. The way we implement the Sobel filter here is by
   * first grayscaling the image, then taking the horizontal and vertical
   * gradients and finally combining the gradient images to make up the final
   * image. Adapted from https://github.com/kig/canvasfilters.
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @return {array} The edge pixels in a linear [r,g,b,a,...] array.
   */
  tracking.Image.sobel = function (pixels, width, height) {
    pixels = this.grayscale(pixels, width, height, true)
    var output = new Float32Array(width * height * 4)
    var sobelSignVector = new Float32Array([-1, 0, 1])
    var sobelScaleVector = new Float32Array([1, 2, 1])
    var vertical = this.separableConvolve(
      pixels,
      width,
      height,
      sobelSignVector,
      sobelScaleVector
    )
    var horizontal = this.separableConvolve(
      pixels,
      width,
      height,
      sobelScaleVector,
      sobelSignVector
    )

    for (var i = 0; i < output.length; i += 4) {
      var v = vertical[i]
      var h = horizontal[i]
      var p = Math.sqrt(h * h + v * v)
      output[i] = p
      output[i + 1] = p
      output[i + 2] = p
      output[i + 3] = 255
    }

    return output
  }

  /**
   * Equalizes the histogram of a grayscale image, normalizing the
   * brightness and increasing the contrast of the image.
   * @param {pixels} pixels The grayscale pixels in a linear array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @return {array} The equalized grayscale pixels in a linear array.
   */
  tracking.Image.equalizeHist = function (pixels, width, height) {
    var equalized = new Uint8ClampedArray(pixels.length)

    var histogram = new Array(256)
    for (var i = 0; i < 256; i++) histogram[i] = 0

    for (var i = 0; i < pixels.length; i++) {
      equalized[i] = pixels[i]
      histogram[pixels[i]]++
    }

    var prev = histogram[0]
    for (var i = 0; i < 256; i++) {
      histogram[i] += prev
      prev = histogram[i]
    }

    var norm = 255 / pixels.length
    for (var i = 0; i < pixels.length; i++)
      equalized[i] = (histogram[pixels[i]] * norm + 0.5) | 0

    return equalized
  }
})()
;(function () {
  /**
   * ViolaJones utility.
   * @static
   * @constructor
   */
  tracking.ViolaJones = {}

  /**
   * Holds the minimum area of intersection that defines when a rectangle is
   * from the same group. Often when a face is matched multiple rectangles are
   * classified as possible rectangles to represent the face, when they
   * intersects they are grouped as one face.
   * @type {number}
   * @default 0.5
   * @static
   */
  tracking.ViolaJones.REGIONS_OVERLAP = 0.5

  /**
   * Holds the HAAR cascade classifiers converted from OpenCV training.
   * @type {array}
   * @static
   */
  tracking.ViolaJones.classifiers = {}

  /**
   * Detects through the HAAR cascade data rectangles matches.
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {number} initialScale The initial scale to start the block
   *     scaling.
   * @param {number} scaleFactor The scale factor to scale the feature block.
   * @param {number} stepSize The block step size.
   * @param {number} edgesDensity Percentage density edges inside the
   *     classifier block. Value from [0.0, 1.0], defaults to 0.2. If specified
   *     edge detection will be applied to the image to prune dead areas of the
   *     image, this can improve significantly performance.
   * @param {number} data The HAAR cascade data.
   * @return {array} Found rectangles.
   * @static
   */
  tracking.ViolaJones.detect = function (
    pixels,
    width,
    height,
    initialScale,
    scaleFactor,
    stepSize,
    edgesDensity,
    data
  ) {
    var total = 0
    var rects = []
    var integralImage = new Int32Array(width * height)
    var integralImageSquare = new Int32Array(width * height)
    var tiltedIntegralImage = new Int32Array(width * height)

    var integralImageSobel
    if (edgesDensity > 0) {
      integralImageSobel = new Int32Array(width * height)
    }

    tracking.Image.computeIntegralImage(
      pixels,
      width,
      height,
      integralImage,
      integralImageSquare,
      tiltedIntegralImage,
      integralImageSobel
    )

    var minWidth = data[0]
    var minHeight = data[1]
    var scale = initialScale * scaleFactor
    var blockWidth = (scale * minWidth) | 0
    var blockHeight = (scale * minHeight) | 0

    while (blockWidth < width && blockHeight < height) {
      var step = (scale * stepSize + 0.5) | 0
      for (var i = 0; i < height - blockHeight; i += step) {
        for (var j = 0; j < width - blockWidth; j += step) {
          if (edgesDensity > 0) {
            if (
              this.isTriviallyExcluded(
                edgesDensity,
                integralImageSobel,
                i,
                j,
                width,
                blockWidth,
                blockHeight
              )
            ) {
              continue
            }
          }

          if (
            this.evalStages_(
              data,
              integralImage,
              integralImageSquare,
              tiltedIntegralImage,
              i,
              j,
              width,
              blockWidth,
              blockHeight,
              scale
            )
          ) {
            rects[total++] = {
              width: blockWidth,
              height: blockHeight,
              x: j,
              y: i,
            }
          }
        }
      }

      scale *= scaleFactor
      blockWidth = (scale * minWidth) | 0
      blockHeight = (scale * minHeight) | 0
    }
    return this.mergeRectangles_(rects)
  }

  /**
   * Fast check to test whether the edges density inside the block is greater
   * than a threshold, if true it tests the stages. This can improve
   * significantly performance.
   * @param {number} edgesDensity Percentage density edges inside the
   *     classifier block.
   * @param {array} integralImageSobel The integral image of a sobel image.
   * @param {number} i Vertical position of the pixel to be evaluated.
   * @param {number} j Horizontal position of the pixel to be evaluated.
   * @param {number} width The image width.
   * @return {boolean} True whether the block at position i,j can be skipped,
   *     false otherwise.
   * @static
   * @protected
   */
  tracking.ViolaJones.isTriviallyExcluded = function (
    edgesDensity,
    integralImageSobel,
    i,
    j,
    width,
    blockWidth,
    blockHeight
  ) {
    var wbA = i * width + j
    var wbB = wbA + blockWidth
    var wbD = wbA + blockHeight * width
    var wbC = wbD + blockWidth
    var blockEdgesDensity =
      (integralImageSobel[wbA] -
        integralImageSobel[wbB] -
        integralImageSobel[wbD] +
        integralImageSobel[wbC]) /
      (blockWidth * blockHeight * 255)
    if (blockEdgesDensity < edgesDensity) {
      return true
    }
    return false
  }

  /**
   * Evaluates if the block size on i,j position is a valid HAAR cascade
   * stage.
   * @param {number} data The HAAR cascade data.
   * @param {number} i Vertical position of the pixel to be evaluated.
   * @param {number} j Horizontal position of the pixel to be evaluated.
   * @param {number} width The image width.
   * @param {number} blockSize The block size.
   * @param {number} scale The scale factor of the block size and its original
   *     size.
   * @param {number} inverseArea The inverse area of the block size.
   * @return {boolean} Whether the region passes all the stage tests.
   * @private
   * @static
   */
  tracking.ViolaJones.evalStages_ = function (
    data,
    integralImage,
    integralImageSquare,
    tiltedIntegralImage,
    i,
    j,
    width,
    blockWidth,
    blockHeight,
    scale
  ) {
    var inverseArea = 1.0 / (blockWidth * blockHeight)
    var wbA = i * width + j
    var wbB = wbA + blockWidth
    var wbD = wbA + blockHeight * width
    var wbC = wbD + blockWidth
    var mean =
      (integralImage[wbA] -
        integralImage[wbB] -
        integralImage[wbD] +
        integralImage[wbC]) *
      inverseArea
    var variance =
      (integralImageSquare[wbA] -
        integralImageSquare[wbB] -
        integralImageSquare[wbD] +
        integralImageSquare[wbC]) *
        inverseArea -
      mean * mean

    var standardDeviation = 1
    if (variance > 0) {
      standardDeviation = Math.sqrt(variance)
    }

    var length = data.length

    for (var w = 2; w < length; ) {
      var stageSum = 0
      var stageThreshold = data[w++]
      var nodeLength = data[w++]

      while (nodeLength--) {
        var rectsSum = 0
        var tilted = data[w++]
        var rectsLength = data[w++]

        for (var r = 0; r < rectsLength; r++) {
          var rectLeft = (j + data[w++] * scale + 0.5) | 0
          var rectTop = (i + data[w++] * scale + 0.5) | 0
          var rectWidth = (data[w++] * scale + 0.5) | 0
          var rectHeight = (data[w++] * scale + 0.5) | 0
          var rectWeight = data[w++]

          var w1
          var w2
          var w3
          var w4
          if (tilted) {
            // RectSum(r) = RSAT(x-h+w, y+w+h-1) + RSAT(x, y-1) - RSAT(x-h, y+h-1) - RSAT(x+w, y+w-1)
            w1 =
              rectLeft -
              rectHeight +
              rectWidth +
              (rectTop + rectWidth + rectHeight - 1) * width
            w2 = rectLeft + (rectTop - 1) * width
            w3 = rectLeft - rectHeight + (rectTop + rectHeight - 1) * width
            w4 = rectLeft + rectWidth + (rectTop + rectWidth - 1) * width
            rectsSum +=
              (tiltedIntegralImage[w1] +
                tiltedIntegralImage[w2] -
                tiltedIntegralImage[w3] -
                tiltedIntegralImage[w4]) *
              rectWeight
          } else {
            // RectSum(r) = SAT(x-1, y-1) + SAT(x+w-1, y+h-1) - SAT(x-1, y+h-1) - SAT(x+w-1, y-1)
            w1 = rectTop * width + rectLeft
            w2 = w1 + rectWidth
            w3 = w1 + rectHeight * width
            w4 = w3 + rectWidth
            rectsSum +=
              (integralImage[w1] -
                integralImage[w2] -
                integralImage[w3] +
                integralImage[w4]) *
              rectWeight
            // TODO: Review the code below to analyze performance when using it instead.
            // w1 = (rectLeft - 1) + (rectTop - 1) * width;
            // w2 = (rectLeft + rectWidth - 1) + (rectTop + rectHeight - 1) * width;
            // w3 = (rectLeft - 1) + (rectTop + rectHeight - 1) * width;
            // w4 = (rectLeft + rectWidth - 1) + (rectTop - 1) * width;
            // rectsSum += (integralImage[w1] + integralImage[w2] - integralImage[w3] - integralImage[w4]) * rectWeight;
          }
        }

        var nodeThreshold = data[w++]
        var nodeLeft = data[w++]
        var nodeRight = data[w++]

        if (rectsSum * inverseArea < nodeThreshold * standardDeviation) {
          stageSum += nodeLeft
        } else {
          stageSum += nodeRight
        }
      }

      if (stageSum < stageThreshold) {
        return false
      }
    }
    return true
  }

  /**
   * Postprocess the detected sub-windows in order to combine overlapping
   * detections into a single detection.
   * @param {array} rects
   * @return {array}
   * @private
   * @static
   */
  tracking.ViolaJones.mergeRectangles_ = function (rects) {
    var disjointSet = new tracking.DisjointSet(rects.length)

    for (var i = 0; i < rects.length; i++) {
      var r1 = rects[i]
      for (var j = 0; j < rects.length; j++) {
        var r2 = rects[j]
        if (
          tracking.Math.intersectRect(
            r1.x,
            r1.y,
            r1.x + r1.width,
            r1.y + r1.height,
            r2.x,
            r2.y,
            r2.x + r2.width,
            r2.y + r2.height
          )
        ) {
          var x1 = Math.max(r1.x, r2.x)
          var y1 = Math.max(r1.y, r2.y)
          var x2 = Math.min(r1.x + r1.width, r2.x + r2.width)
          var y2 = Math.min(r1.y + r1.height, r2.y + r2.height)
          var overlap = (x1 - x2) * (y1 - y2)
          var area1 = r1.width * r1.height
          var area2 = r2.width * r2.height

          if (
            overlap / (area1 * (area1 / area2)) >= this.REGIONS_OVERLAP &&
            overlap / (area2 * (area1 / area2)) >= this.REGIONS_OVERLAP
          ) {
            disjointSet.union(i, j)
          }
        }
      }
    }

    var map = {}
    for (var k = 0; k < disjointSet.length; k++) {
      var rep = disjointSet.find(k)
      if (!map[rep]) {
        map[rep] = {
          total: 1,
          width: rects[k].width,
          height: rects[k].height,
          x: rects[k].x,
          y: rects[k].y,
        }
        continue
      }
      map[rep].total++
      map[rep].width += rects[k].width
      map[rep].height += rects[k].height
      map[rep].x += rects[k].x
      map[rep].y += rects[k].y
    }

    var result = []
    Object.keys(map).forEach(function (key) {
      var rect = map[key]
      result.push({
        total: rect.total,
        width: (rect.width / rect.total + 0.5) | 0,
        height: (rect.height / rect.total + 0.5) | 0,
        x: (rect.x / rect.total + 0.5) | 0,
        y: (rect.y / rect.total + 0.5) | 0,
      })
    })

    return result
  }
})()
;(function () {
  /**
   * Brief intends for "Binary Robust Independent Elementary Features".This
   * method generates a binary string for each keypoint found by an extractor
   * method.
   * @static
   * @constructor
   */
  tracking.Brief = {}

  /**
   * The set of binary tests is defined by the nd (x,y)-location pairs
   * uniquely chosen during the initialization. Values could vary between N =
   * 128,256,512. N=128 yield good compromises between speed, storage
   * efficiency, and recognition rate.
   * @type {number}
   */
  tracking.Brief.N = 512

  /**
   * Caches coordinates values of (x,y)-location pairs uniquely chosen during
   * the initialization.
   * @type {Object.<number, Int32Array>}
   * @private
   * @static
   */
  tracking.Brief.randomImageOffsets_ = {}

  /**
   * Caches delta values of (x,y)-location pairs uniquely chosen during
   * the initialization.
   * @type {Int32Array}
   * @private
   * @static
   */
  tracking.Brief.randomWindowOffsets_ = null

  /**
   * Generates a binary string for each found keypoints extracted using an
   * extractor method.
   * @param {array} The grayscale pixels in a linear [p1,p2,...] array.
   * @param {number} width The image width.
   * @param {array} keypoints
   * @return {Int32Array} Returns an array where for each four sequence int
   *     values represent the descriptor binary string (128 bits) necessary
   *     to describe the corner, e.g. [0,0,0,0, 0,0,0,0, ...].
   * @static
   */
  tracking.Brief.getDescriptors = function (pixels, width, keypoints) {
    // Optimizing divide by 32 operation using binary shift
    // (this.N >> 5) === this.N/32.
    var descriptors = new Int32Array((keypoints.length >> 1) * (this.N >> 5))
    var descriptorWord = 0
    var offsets = this.getRandomOffsets_(width)
    var position = 0

    for (var i = 0; i < keypoints.length; i += 2) {
      var w = width * keypoints[i + 1] + keypoints[i]

      var offsetsPosition = 0
      for (var j = 0, n = this.N; j < n; j++) {
        if (
          pixels[offsets[offsetsPosition++] + w] <
          pixels[offsets[offsetsPosition++] + w]
        ) {
          // The bit in the position `j % 32` of descriptorWord should be set to 1. We do
          // this by making an OR operation with a binary number that only has the bit
          // in that position set to 1. That binary number is obtained by shifting 1 left by
          // `j % 32` (which is the same as `j & 31` left) positions.
          descriptorWord |= 1 << (j & 31)
        }

        // If the next j is a multiple of 32, we will need to use a new descriptor word to hold
        // the next results.
        if (!((j + 1) & 31)) {
          descriptors[position++] = descriptorWord
          descriptorWord = 0
        }
      }
    }

    return descriptors
  }

  /**
   * Matches sets of features {mi} and {m′j} extracted from two images taken
   * from similar, and often successive, viewpoints. A classical procedure
   * runs as follows. For each point {mi} in the first image, search in a
   * region of the second image around location {mi} for point {m′j}. The
   * search is based on the similarity of the local image windows, also known
   * as kernel windows, centered on the points, which strongly characterizes
   * the points when the images are sufficiently close. Once each keypoint is
   * described with its binary string, they need to be compared with the
   * closest matching point. Distance metric is critical to the performance of
   * in- trusion detection systems. Thus using binary strings reduces the size
   * of the descriptor and provides an interesting data structure that is fast
   * to operate whose similarity can be measured by the Hamming distance.
   * @param {array} keypoints1
   * @param {array} descriptors1
   * @param {array} keypoints2
   * @param {array} descriptors2
   * @return {Int32Array} Returns an array where the index is the corner1
   *     index coordinate, and the value is the corresponding match index of
   *     corner2, e.g. keypoints1=[x0,y0,x1,y1,...] and
   *     keypoints2=[x'0,y'0,x'1,y'1,...], if x0 matches x'1 and x1 matches x'0,
   *     the return array would be [3,0].
   * @static
   */
  tracking.Brief.match = function (
    keypoints1,
    descriptors1,
    keypoints2,
    descriptors2
  ) {
    var len1 = keypoints1.length >> 1
    var len2 = keypoints2.length >> 1
    var matches = new Array(len1)

    for (var i = 0; i < len1; i++) {
      var min = Infinity
      var minj = 0
      for (var j = 0; j < len2; j++) {
        var dist = 0
        // Optimizing divide by 32 operation using binary shift
        // (this.N >> 5) === this.N/32.
        for (var k = 0, n = this.N >> 5; k < n; k++) {
          dist += tracking.Math.hammingWeight(
            descriptors1[i * n + k] ^ descriptors2[j * n + k]
          )
        }
        if (dist < min) {
          min = dist
          minj = j
        }
      }
      matches[i] = {
        index1: i,
        index2: minj,
        keypoint1: [keypoints1[2 * i], keypoints1[2 * i + 1]],
        keypoint2: [keypoints2[2 * minj], keypoints2[2 * minj + 1]],
        confidence: 1 - min / this.N,
      }
    }

    return matches
  }

  /**
   * Removes matches outliers by testing matches on both directions.
   * @param {array} keypoints1
   * @param {array} descriptors1
   * @param {array} keypoints2
   * @param {array} descriptors2
   * @return {Int32Array} Returns an array where the index is the corner1
   *     index coordinate, and the value is the corresponding match index of
   *     corner2, e.g. keypoints1=[x0,y0,x1,y1,...] and
   *     keypoints2=[x'0,y'0,x'1,y'1,...], if x0 matches x'1 and x1 matches x'0,
   *     the return array would be [3,0].
   * @static
   */
  tracking.Brief.reciprocalMatch = function (
    keypoints1,
    descriptors1,
    keypoints2,
    descriptors2
  ) {
    var matches = []
    if (keypoints1.length === 0 || keypoints2.length === 0) {
      return matches
    }

    var matches1 = tracking.Brief.match(
      keypoints1,
      descriptors1,
      keypoints2,
      descriptors2
    )
    var matches2 = tracking.Brief.match(
      keypoints2,
      descriptors2,
      keypoints1,
      descriptors1
    )
    for (var i = 0; i < matches1.length; i++) {
      if (matches2[matches1[i].index2].index2 === i) {
        matches.push(matches1[i])
      }
    }
    return matches
  }

  /**
   * Gets the coordinates values of (x,y)-location pairs uniquely chosen
   * during the initialization.
   * @return {array} Array with the random offset values.
   * @private
   */
  tracking.Brief.getRandomOffsets_ = function (width) {
    if (!this.randomWindowOffsets_) {
      var windowPosition = 0
      var windowOffsets = new Int32Array(4 * this.N)
      for (var i = 0; i < this.N; i++) {
        windowOffsets[windowPosition++] = Math.round(
          tracking.Math.uniformRandom(-15, 16)
        )
        windowOffsets[windowPosition++] = Math.round(
          tracking.Math.uniformRandom(-15, 16)
        )
        windowOffsets[windowPosition++] = Math.round(
          tracking.Math.uniformRandom(-15, 16)
        )
        windowOffsets[windowPosition++] = Math.round(
          tracking.Math.uniformRandom(-15, 16)
        )
      }
      this.randomWindowOffsets_ = windowOffsets
    }

    if (!this.randomImageOffsets_[width]) {
      var imagePosition = 0
      var imageOffsets = new Int32Array(2 * this.N)
      for (var j = 0; j < this.N; j++) {
        imageOffsets[imagePosition++] =
          this.randomWindowOffsets_[4 * j] * width +
          this.randomWindowOffsets_[4 * j + 1]
        imageOffsets[imagePosition++] =
          this.randomWindowOffsets_[4 * j + 2] * width +
          this.randomWindowOffsets_[4 * j + 3]
      }
      this.randomImageOffsets_[width] = imageOffsets
    }

    return this.randomImageOffsets_[width]
  }
})()
;(function () {
  /**
   * FAST intends for "Features from Accelerated Segment Test". This method
   * performs a point segment test corner detection. The segment test
   * criterion operates by considering a circle of sixteen pixels around the
   * corner candidate p. The detector classifies p as a corner if there exists
   * a set of n contiguous pixelsin the circle which are all brighter than the
   * intensity of the candidate pixel Ip plus a threshold t, or all darker
   * than Ip − t.
   *
   *       15 00 01
   *    14          02
   * 13                03
   * 12       []       04
   * 11                05
   *    10          06
   *       09 08 07
   *
   * For more reference:
   * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.60.3991&rep=rep1&type=pdf
   * @static
   * @constructor
   */
  tracking.Fast = {}

  /**
   * Holds the threshold to determine whether the tested pixel is brighter or
   * darker than the corner candidate p.
   * @type {number}
   * @default 40
   * @static
   */
  tracking.Fast.THRESHOLD = 40

  /**
   * Caches coordinates values of the circle surrounding the pixel candidate p.
   * @type {Object.<number, Int32Array>}
   * @private
   * @static
   */
  tracking.Fast.circles_ = {}

  /**
   * Finds corners coordinates on the graysacaled image.
   * @param {array} The grayscale pixels in a linear [p1,p2,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {number} threshold to determine whether the tested pixel is brighter or
   *     darker than the corner candidate p. Default value is 40.
   * @return {array} Array containing the coordinates of all found corners,
   *     e.g. [x0,y0,x1,y1,...], where P(x0,y0) represents a corner coordinate.
   * @static
   */
  tracking.Fast.findCorners = function (pixels, width, height, opt_threshold) {
    var circleOffsets = this.getCircleOffsets_(width)
    var circlePixels = new Int32Array(16)
    var corners = []

    if (opt_threshold === undefined) {
      opt_threshold = this.THRESHOLD
    }

    // When looping through the image pixels, skips the first three lines from
    // the image boundaries to constrain the surrounding circle inside the image
    // area.
    for (var i = 3; i < height - 3; i++) {
      for (var j = 3; j < width - 3; j++) {
        var w = i * width + j
        var p = pixels[w]

        // Loops the circle offsets to read the pixel value for the sixteen
        // surrounding pixels.
        for (var k = 0; k < 16; k++) {
          circlePixels[k] = pixels[w + circleOffsets[k]]
        }

        if (this.isCorner(p, circlePixels, opt_threshold)) {
          // The pixel p is classified as a corner, as optimization increment j
          // by the circle radius 3 to skip the neighbor pixels inside the
          // surrounding circle. This can be removed without compromising the
          // result.
          corners.push(j, i)
          j += 3
        }
      }
    }

    return corners
  }

  /**
   * Checks if the circle pixel is brighter than the candidate pixel p by
   * a threshold.
   * @param {number} circlePixel The circle pixel value.
   * @param {number} p The value of the candidate pixel p.
   * @param {number} threshold
   * @return {Boolean}
   * @static
   */
  tracking.Fast.isBrighter = function (circlePixel, p, threshold) {
    return circlePixel - p > threshold
  }

  /**
   * Checks if the circle pixel is within the corner of the candidate pixel p
   * by a threshold.
   * @param {number} p The value of the candidate pixel p.
   * @param {number} circlePixel The circle pixel value.
   * @param {number} threshold
   * @return {Boolean}
   * @static
   */
  tracking.Fast.isCorner = function (p, circlePixels, threshold) {
    if (this.isTriviallyExcluded(circlePixels, p, threshold)) {
      return false
    }

    for (var x = 0; x < 16; x++) {
      var darker = true
      var brighter = true

      for (var y = 0; y < 9; y++) {
        var circlePixel = circlePixels[(x + y) & 15]

        if (!this.isBrighter(p, circlePixel, threshold)) {
          brighter = false
          if (darker === false) {
            break
          }
        }

        if (!this.isDarker(p, circlePixel, threshold)) {
          darker = false
          if (brighter === false) {
            break
          }
        }
      }

      if (brighter || darker) {
        return true
      }
    }

    return false
  }

  /**
   * Checks if the circle pixel is darker than the candidate pixel p by
   * a threshold.
   * @param {number} circlePixel The circle pixel value.
   * @param {number} p The value of the candidate pixel p.
   * @param {number} threshold
   * @return {Boolean}
   * @static
   */
  tracking.Fast.isDarker = function (circlePixel, p, threshold) {
    return p - circlePixel > threshold
  }

  /**
   * Fast check to test if the candidate pixel is a trivially excluded value.
   * In order to be a corner, the candidate pixel value should be darker or
   * brighter than 9-12 surrounding pixels, when at least three of the top,
   * bottom, left and right pixels are brighter or darker it can be
   * automatically excluded improving the performance.
   * @param {number} circlePixel The circle pixel value.
   * @param {number} p The value of the candidate pixel p.
   * @param {number} threshold
   * @return {Boolean}
   * @static
   * @protected
   */
  tracking.Fast.isTriviallyExcluded = function (circlePixels, p, threshold) {
    var count = 0
    var circleBottom = circlePixels[8]
    var circleLeft = circlePixels[12]
    var circleRight = circlePixels[4]
    var circleTop = circlePixels[0]

    if (this.isBrighter(circleTop, p, threshold)) {
      count++
    }
    if (this.isBrighter(circleRight, p, threshold)) {
      count++
    }
    if (this.isBrighter(circleBottom, p, threshold)) {
      count++
    }
    if (this.isBrighter(circleLeft, p, threshold)) {
      count++
    }

    if (count < 3) {
      count = 0
      if (this.isDarker(circleTop, p, threshold)) {
        count++
      }
      if (this.isDarker(circleRight, p, threshold)) {
        count++
      }
      if (this.isDarker(circleBottom, p, threshold)) {
        count++
      }
      if (this.isDarker(circleLeft, p, threshold)) {
        count++
      }
      if (count < 3) {
        return true
      }
    }

    return false
  }

  /**
   * Gets the sixteen offset values of the circle surrounding pixel.
   * @param {number} width The image width.
   * @return {array} Array with the sixteen offset values of the circle
   *     surrounding pixel.
   * @private
   */
  tracking.Fast.getCircleOffsets_ = function (width) {
    if (this.circles_[width]) {
      return this.circles_[width]
    }

    var circle = new Int32Array(16)

    circle[0] = -width - width - width
    circle[1] = circle[0] + 1
    circle[2] = circle[1] + width + 1
    circle[3] = circle[2] + width + 1
    circle[4] = circle[3] + width
    circle[5] = circle[4] + width
    circle[6] = circle[5] + width - 1
    circle[7] = circle[6] + width - 1
    circle[8] = circle[7] - 1
    circle[9] = circle[8] - 1
    circle[10] = circle[9] - width - 1
    circle[11] = circle[10] - width - 1
    circle[12] = circle[11] - width
    circle[13] = circle[12] - width
    circle[14] = circle[13] - width + 1
    circle[15] = circle[14] - width + 1

    this.circles_[width] = circle
    return circle
  }
})()
;(function () {
  /**
   * Math utility.
   * @static
   * @constructor
   */
  tracking.Math = {}

  /**
   * Euclidean distance between two points P(x0, y0) and P(x1, y1).
   * @param {number} x0 Horizontal coordinate of P0.
   * @param {number} y0 Vertical coordinate of P0.
   * @param {number} x1 Horizontal coordinate of P1.
   * @param {number} y1 Vertical coordinate of P1.
   * @return {number} The euclidean distance.
   */
  tracking.Math.distance = function (x0, y0, x1, y1) {
    var dx = x1 - x0
    var dy = y1 - y0

    return Math.sqrt(dx * dx + dy * dy)
  }

  /**
   * Calculates the Hamming weight of a string, which is the number of symbols that are
   * different from the zero-symbol of the alphabet used. It is thus
   * equivalent to the Hamming distance from the all-zero string of the same
   * length. For the most typical case, a string of bits, this is the number
   * of 1's in the string.
   *
   * Example:
   *
   * <pre>
   *  Binary string     Hamming weight
   *   11101                 4
   *   11101010              5
   * </pre>
   *
   * @param {number} i Number that holds the binary string to extract the hamming weight.
   * @return {number} The hamming weight.
   */
  tracking.Math.hammingWeight = function (i) {
    i = i - ((i >> 1) & 0x55555555)
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333)

    return (((i + (i >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24
  }

  /**
   * Generates a random number between [a, b] interval.
   * @param {number} a
   * @param {number} b
   * @return {number}
   */
  tracking.Math.uniformRandom = function (a, b) {
    return a + Math.random() * (b - a)
  }

  /**
   * Tests if a rectangle intersects with another.
   *
   *  <pre>
   *  x0y0 --------       x2y2 --------
   *      |       |           |       |
   *      -------- x1y1       -------- x3y3
   * </pre>
   *
   * @param {number} x0 Horizontal coordinate of P0.
   * @param {number} y0 Vertical coordinate of P0.
   * @param {number} x1 Horizontal coordinate of P1.
   * @param {number} y1 Vertical coordinate of P1.
   * @param {number} x2 Horizontal coordinate of P2.
   * @param {number} y2 Vertical coordinate of P2.
   * @param {number} x3 Horizontal coordinate of P3.
   * @param {number} y3 Vertical coordinate of P3.
   * @return {boolean}
   */
  tracking.Math.intersectRect = function (x0, y0, x1, y1, x2, y2, x3, y3) {
    return !(x2 > x1 || x3 < x0 || y2 > y1 || y3 < y0)
  }
})()
;(function () {
  /**
   * Matrix utility.
   * @static
   * @constructor
   */
  tracking.Matrix = {}

  /**
   * Loops the array organized as major-row order and executes `fn` callback
   * for each iteration. The `fn` callback receives the following parameters:
   * `(r,g,b,a,index,i,j)`, where `r,g,b,a` represents the pixel color with
   * alpha channel, `index` represents the position in the major-row order
   * array and `i,j` the respective indexes positions in two dimensions.
   * @param {array} pixels The pixels in a linear [r,g,b,a,...] array to loop
   *     through.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {function} fn The callback function for each pixel.
   * @param {number} opt_jump Optional jump for the iteration, by default it
   *     is 1, hence loops all the pixels of the array.
   * @static
   */
  tracking.Matrix.forEach = function (pixels, width, height, fn, opt_jump) {
    opt_jump = opt_jump || 1
    for (var i = 0; i < height; i += opt_jump) {
      for (var j = 0; j < width; j += opt_jump) {
        var w = i * width * 4 + j * 4
        fn.call(
          this,
          pixels[w],
          pixels[w + 1],
          pixels[w + 2],
          pixels[w + 3],
          w,
          i,
          j
        )
      }
    }
  }

  /**
   * Calculates the per-element subtraction of two NxM matrices and returns a
   * new NxM matrix as the result.
   * @param {matrix} a The first matrix.
   * @param {matrix} a The second matrix.
   * @static
   */
  tracking.Matrix.sub = function (a, b) {
    var res = tracking.Matrix.clone(a)
    for (var i = 0; i < res.length; i++) {
      for (var j = 0; j < res[i].length; j++) {
        res[i][j] -= b[i][j]
      }
    }
    return res
  }

  /**
   * Calculates the per-element sum of two NxM matrices and returns a new NxM
   * NxM matrix as the result.
   * @param {matrix} a The first matrix.
   * @param {matrix} a The second matrix.
   * @static
   */
  tracking.Matrix.add = function (a, b) {
    var res = tracking.Matrix.clone(a)
    for (var i = 0; i < res.length; i++) {
      for (var j = 0; j < res[i].length; j++) {
        res[i][j] += b[i][j]
      }
    }
    return res
  }

  /**
   * Clones a matrix (or part of it) and returns a new matrix as the result.
   * @param {matrix} src The matrix to be cloned.
   * @param {number} width The second matrix.
   * @static
   */
  tracking.Matrix.clone = function (src, width, height) {
    width = width || src[0].length
    height = height || src.length
    var temp = new Array(height)
    var i = height
    while (i--) {
      temp[i] = new Array(width)
      var j = width
      while (j--) temp[i][j] = src[i][j]
    }
    return temp
  }

  /**
   * Multiply a matrix by a scalar and returns a new matrix as the result.
   * @param {number} scalar The scalar to multiply the matrix by.
   * @param {matrix} src The matrix to be multiplied.
   * @static
   */
  tracking.Matrix.mulScalar = function (scalar, src) {
    var res = tracking.Matrix.clone(src)
    for (var i = 0; i < src.length; i++) {
      for (var j = 0; j < src[i].length; j++) {
        res[i][j] *= scalar
      }
    }
    return res
  }

  /**
   * Transpose a matrix and returns a new matrix as the result.
   * @param {matrix} src The matrix to be transposed.
   * @static
   */
  tracking.Matrix.transpose = function (src) {
    var transpose = new Array(src[0].length)
    for (var i = 0; i < src[0].length; i++) {
      transpose[i] = new Array(src.length)
      for (var j = 0; j < src.length; j++) {
        transpose[i][j] = src[j][i]
      }
    }
    return transpose
  }

  /**
   * Multiply an MxN matrix with an NxP matrix and returns a new MxP matrix
   * as the result.
   * @param {matrix} a The first matrix.
   * @param {matrix} b The second matrix.
   * @static
   */
  tracking.Matrix.mul = function (a, b) {
    var res = new Array(a.length)
    for (var i = 0; i < a.length; i++) {
      res[i] = new Array(b[0].length)
      for (var j = 0; j < b[0].length; j++) {
        res[i][j] = 0
        for (var k = 0; k < a[0].length; k++) {
          res[i][j] += a[i][k] * b[k][j]
        }
      }
    }
    return res
  }

  /**
   * Calculates the absolute norm of a matrix.
   * @param {matrix} src The matrix which norm will be calculated.
   * @static
   */
  tracking.Matrix.norm = function (src) {
    var res = 0
    for (var i = 0; i < src.length; i++) {
      for (var j = 0; j < src[i].length; j++) {
        res += src[i][j] * src[i][j]
      }
    }
    return Math.sqrt(res)
  }

  /**
   * Calculates and returns the covariance matrix of a set of vectors as well
   * as the mean of the matrix.
   * @param {matrix} src The matrix which covariance matrix will be calculated.
   * @static
   */
  tracking.Matrix.calcCovarMatrix = function (src) {
    var mean = new Array(src.length)
    for (var i = 0; i < src.length; i++) {
      mean[i] = [0.0]
      for (var j = 0; j < src[i].length; j++) {
        mean[i][0] += src[i][j] / src[i].length
      }
    }

    var deltaFull = tracking.Matrix.clone(mean)
    for (var i = 0; i < deltaFull.length; i++) {
      for (var j = 0; j < src[0].length - 1; j++) {
        deltaFull[i].push(deltaFull[i][0])
      }
    }

    var a = tracking.Matrix.sub(src, deltaFull)
    var b = tracking.Matrix.transpose(a)
    var covar = tracking.Matrix.mul(b, a)
    return [covar, mean]
  }
})()
;(function () {
  /**
   * EPnp utility.
   * @static
   * @constructor
   */
  tracking.EPnP = {}

  tracking.EPnP.solve = function (objectPoints, imagePoints, cameraMatrix) {}
})()
;(function () {
  /**
   * Tracker utility.
   * @constructor
   * @extends {tracking.EventEmitter}
   */
  tracking.Tracker = function () {
    tracking.Tracker.base(this, 'constructor')
  }

  tracking.inherits(tracking.Tracker, tracking.EventEmitter)

  /**
   * Tracks the pixels on the array. This method is called for each video
   * frame in order to emit `track` event.
   * @param {Uint8ClampedArray} pixels The pixels data to track.
   * @param {number} width The pixels canvas width.
   * @param {number} height The pixels canvas height.
   */
  tracking.Tracker.prototype.track = function () {}
})()
;(function () {
  /**
   * TrackerTask utility.
   * @constructor
   * @extends {tracking.EventEmitter}
   */
  tracking.TrackerTask = function (tracker) {
    tracking.TrackerTask.base(this, 'constructor')

    if (!tracker) {
      throw new Error('Tracker instance not specified.')
    }

    this.setTracker(tracker)
  }

  tracking.inherits(tracking.TrackerTask, tracking.EventEmitter)

  /**
   * Holds the tracker instance managed by this task.
   * @type {tracking.Tracker}
   * @private
   */
  tracking.TrackerTask.prototype.tracker_ = null

  /**
   * Holds if the tracker task is in running.
   * @type {boolean}
   * @private
   */
  tracking.TrackerTask.prototype.running_ = false

  /**
   * Gets the tracker instance managed by this task.
   * @return {tracking.Tracker}
   */
  tracking.TrackerTask.prototype.getTracker = function () {
    return this.tracker_
  }

  /**
   * Returns true if the tracker task is in running, false otherwise.
   * @return {boolean}
   * @private
   */
  tracking.TrackerTask.prototype.inRunning = function () {
    return this.running_
  }

  /**
   * Sets if the tracker task is in running.
   * @param {boolean} running
   * @private
   */
  tracking.TrackerTask.prototype.setRunning = function (running) {
    this.running_ = running
  }

  /**
   * Sets the tracker instance managed by this task.
   * @return {tracking.Tracker}
   */
  tracking.TrackerTask.prototype.setTracker = function (tracker) {
    this.tracker_ = tracker
  }

  /**
   * Emits a `run` event on the tracker task for the implementers to run any
   * child action, e.g. `requestAnimationFrame`.
   * @return {object} Returns itself, so calls can be chained.
   */
  tracking.TrackerTask.prototype.run = function () {
    var self = this

    if (this.inRunning()) {
      return
    }

    this.setRunning(true)
    this.reemitTrackEvent_ = function (event) {
      self.emit('track', event)
    }
    this.tracker_.on('track', this.reemitTrackEvent_)
    this.emit('run')
    return this
  }

  /**
   * Emits a `stop` event on the tracker task for the implementers to stop any
   * child action being done, e.g. `requestAnimationFrame`.
   * @return {object} Returns itself, so calls can be chained.
   */
  tracking.TrackerTask.prototype.stop = function () {
    if (!this.inRunning()) {
      return
    }

    this.setRunning(false)
    this.emit('stop')
    this.tracker_.removeListener('track', this.reemitTrackEvent_)
    return this
  }
})()
;(function () {
  /**
   * ColorTracker utility to track colored blobs in a frame using color
   * difference evaluation.
   * @constructor
   * @param {string|Array.<string>} opt_colors Optional colors to track.
   * @extends {tracking.Tracker}
   */
  tracking.ColorTracker = function (opt_colors) {
    tracking.ColorTracker.base(this, 'constructor')

    if (typeof opt_colors === 'string') {
      opt_colors = [opt_colors]
    }

    if (opt_colors) {
      opt_colors.forEach(function (color) {
        if (!tracking.ColorTracker.getColor(color)) {
          throw new Error(
            'Color not valid, try `new tracking.ColorTracker("magenta")`.'
          )
        }
      })
      this.setColors(opt_colors)
    }
  }

  tracking.inherits(tracking.ColorTracker, tracking.Tracker)

  /**
   * Holds the known colors.
   * @type {Object.<string, function>}
   * @private
   * @static
   */
  tracking.ColorTracker.knownColors_ = {}

  /**
   * Caches coordinates values of the neighbours surrounding a pixel.
   * @type {Object.<number, Int32Array>}
   * @private
   * @static
   */
  tracking.ColorTracker.neighbours_ = {}

  /**
   * Registers a color as known color.
   * @param {string} name The color name.
   * @param {function} fn The color function to test if the passed (r,g,b) is
   *     the desired color.
   * @static
   */
  tracking.ColorTracker.registerColor = function (name, fn) {
    tracking.ColorTracker.knownColors_[name] = fn
  }

  /**
   * Gets the known color function that is able to test whether an (r,g,b) is
   * the desired color.
   * @param {string} name The color name.
   * @return {function} The known color test function.
   * @static
   */
  tracking.ColorTracker.getColor = function (name) {
    return tracking.ColorTracker.knownColors_[name]
  }

  /**
   * Holds the colors to be tracked by the `ColorTracker` instance.
   * @default ['magenta']
   * @type {Array.<string>}
   */
  tracking.ColorTracker.prototype.colors = ['magenta']

  /**
   * Holds the minimum dimension to classify a rectangle.
   * @default 20
   * @type {number}
   */
  tracking.ColorTracker.prototype.minDimension = 20

  /**
   * Holds the maximum dimension to classify a rectangle.
   * @default Infinity
   * @type {number}
   */
  tracking.ColorTracker.prototype.maxDimension = Infinity

  /**
   * Holds the minimum group size to be classified as a rectangle.
   * @default 30
   * @type {number}
   */
  tracking.ColorTracker.prototype.minGroupSize = 30

  /**
   * Calculates the central coordinate from the cloud points. The cloud points
   * are all points that matches the desired color.
   * @param {Array.<number>} cloud Major row order array containing all the
   *     points from the desired color, e.g. [x1, y1, c2, y2, ...].
   * @param {number} total Total numbers of pixels of the desired color.
   * @return {object} Object containing the x, y and estimated z coordinate of
   *     the blog extracted from the cloud points.
   * @private
   */
  tracking.ColorTracker.prototype.calculateDimensions_ = function (
    cloud,
    total
  ) {
    var maxx = -1
    var maxy = -1
    var minx = Infinity
    var miny = Infinity

    for (var c = 0; c < total; c += 2) {
      var x = cloud[c]
      var y = cloud[c + 1]

      if (x < minx) {
        minx = x
      }
      if (x > maxx) {
        maxx = x
      }
      if (y < miny) {
        miny = y
      }
      if (y > maxy) {
        maxy = y
      }
    }

    return {
      width: maxx - minx,
      height: maxy - miny,
      x: minx,
      y: miny,
    }
  }

  /**
   * Gets the colors being tracked by the `ColorTracker` instance.
   * @return {Array.<string>}
   */
  tracking.ColorTracker.prototype.getColors = function () {
    return this.colors
  }

  /**
   * Gets the minimum dimension to classify a rectangle.
   * @return {number}
   */
  tracking.ColorTracker.prototype.getMinDimension = function () {
    return this.minDimension
  }

  /**
   * Gets the maximum dimension to classify a rectangle.
   * @return {number}
   */
  tracking.ColorTracker.prototype.getMaxDimension = function () {
    return this.maxDimension
  }

  /**
   * Gets the minimum group size to be classified as a rectangle.
   * @return {number}
   */
  tracking.ColorTracker.prototype.getMinGroupSize = function () {
    return this.minGroupSize
  }

  /**
   * Gets the eight offset values of the neighbours surrounding a pixel.
   * @param {number} width The image width.
   * @return {array} Array with the eight offset values of the neighbours
   *     surrounding a pixel.
   * @private
   */
  tracking.ColorTracker.prototype.getNeighboursForWidth_ = function (width) {
    if (tracking.ColorTracker.neighbours_[width]) {
      return tracking.ColorTracker.neighbours_[width]
    }

    var neighbours = new Int32Array(8)

    neighbours[0] = -width * 4
    neighbours[1] = -width * 4 + 4
    neighbours[2] = 4
    neighbours[3] = width * 4 + 4
    neighbours[4] = width * 4
    neighbours[5] = width * 4 - 4
    neighbours[6] = -4
    neighbours[7] = -width * 4 - 4

    tracking.ColorTracker.neighbours_[width] = neighbours

    return neighbours
  }

  /**
   * Unites groups whose bounding box intersect with each other.
   * @param {Array.<Object>} rects
   * @private
   */
  tracking.ColorTracker.prototype.mergeRectangles_ = function (rects) {
    var intersects
    var results = []
    var minDimension = this.getMinDimension()
    var maxDimension = this.getMaxDimension()

    for (var r = 0; r < rects.length; r++) {
      var r1 = rects[r]
      intersects = true
      for (var s = r + 1; s < rects.length; s++) {
        var r2 = rects[s]
        if (
          tracking.Math.intersectRect(
            r1.x,
            r1.y,
            r1.x + r1.width,
            r1.y + r1.height,
            r2.x,
            r2.y,
            r2.x + r2.width,
            r2.y + r2.height
          )
        ) {
          intersects = false
          var x1 = Math.min(r1.x, r2.x)
          var y1 = Math.min(r1.y, r2.y)
          var x2 = Math.max(r1.x + r1.width, r2.x + r2.width)
          var y2 = Math.max(r1.y + r1.height, r2.y + r2.height)
          r2.height = y2 - y1
          r2.width = x2 - x1
          r2.x = x1
          r2.y = y1
          break
        }
      }

      if (intersects) {
        if (r1.width >= minDimension && r1.height >= minDimension) {
          if (r1.width <= maxDimension && r1.height <= maxDimension) {
            results.push(r1)
          }
        }
      }
    }

    return results
  }

  /**
   * Sets the colors to be tracked by the `ColorTracker` instance.
   * @param {Array.<string>} colors
   */
  tracking.ColorTracker.prototype.setColors = function (colors) {
    this.colors = colors
  }

  /**
   * Sets the minimum dimension to classify a rectangle.
   * @param {number} minDimension
   */
  tracking.ColorTracker.prototype.setMinDimension = function (minDimension) {
    this.minDimension = minDimension
  }

  /**
   * Sets the maximum dimension to classify a rectangle.
   * @param {number} maxDimension
   */
  tracking.ColorTracker.prototype.setMaxDimension = function (maxDimension) {
    this.maxDimension = maxDimension
  }

  /**
   * Sets the minimum group size to be classified as a rectangle.
   * @param {number} minGroupSize
   */
  tracking.ColorTracker.prototype.setMinGroupSize = function (minGroupSize) {
    this.minGroupSize = minGroupSize
  }

  /**
   * Tracks the `Video` frames. This method is called for each video frame in
   * order to emit `track` event.
   * @param {Uint8ClampedArray} pixels The pixels data to track.
   * @param {number} width The pixels canvas width.
   * @param {number} height The pixels canvas height.
   */
  tracking.ColorTracker.prototype.track = function (pixels, width, height) {
    var self = this
    var colors = this.getColors()

    if (!colors) {
      throw new Error(
        'Colors not specified, try `new tracking.ColorTracker("magenta")`.'
      )
    }

    var results = []

    colors.forEach(function (color) {
      results = results.concat(self.trackColor_(pixels, width, height, color))
    })

    this.emit('track', {
      data: results,
    })
  }

  /**
   * Find the given color in the given matrix of pixels using Flood fill
   * algorithm to determines the area connected to a given node in a
   * multi-dimensional array.
   * @param {Uint8ClampedArray} pixels The pixels data to track.
   * @param {number} width The pixels canvas width.
   * @param {number} height The pixels canvas height.
   * @param {string} color The color to be found
   * @private
   */
  tracking.ColorTracker.prototype.trackColor_ = function (
    pixels,
    width,
    height,
    color
  ) {
    var colorFn = tracking.ColorTracker.knownColors_[color]
    var currGroup = new Int32Array(pixels.length >> 2)
    var currGroupSize
    var currI
    var currJ
    var currW
    var marked = new Int8Array(pixels.length)
    var minGroupSize = this.getMinGroupSize()
    var neighboursW = this.getNeighboursForWidth_(width)
    var queue = new Int32Array(pixels.length)
    var queuePosition
    var results = []
    var w = -4

    if (!colorFn) {
      return results
    }

    for (var i = 0; i < height; i++) {
      for (var j = 0; j < width; j++) {
        w += 4

        if (marked[w]) {
          continue
        }

        currGroupSize = 0

        queuePosition = -1
        queue[++queuePosition] = w
        queue[++queuePosition] = i
        queue[++queuePosition] = j

        marked[w] = 1

        while (queuePosition >= 0) {
          currJ = queue[queuePosition--]
          currI = queue[queuePosition--]
          currW = queue[queuePosition--]

          if (
            colorFn(
              pixels[currW],
              pixels[currW + 1],
              pixels[currW + 2],
              pixels[currW + 3],
              currW,
              currI,
              currJ
            )
          ) {
            currGroup[currGroupSize++] = currJ
            currGroup[currGroupSize++] = currI

            for (var k = 0; k < neighboursW.length; k++) {
              var otherW = currW + neighboursW[k]
              var otherI = currI + neighboursI[k]
              var otherJ = currJ + neighboursJ[k]
              if (
                !marked[otherW] &&
                otherI >= 0 &&
                otherI < height &&
                otherJ >= 0 &&
                otherJ < width
              ) {
                queue[++queuePosition] = otherW
                queue[++queuePosition] = otherI
                queue[++queuePosition] = otherJ

                marked[otherW] = 1
              }
            }
          }
        }

        if (currGroupSize >= minGroupSize) {
          var data = this.calculateDimensions_(currGroup, currGroupSize)
          if (data) {
            data.color = color
            results.push(data)
          }
        }
      }
    }

    return this.mergeRectangles_(results)
  }

  // Default colors
  //===================

  tracking.ColorTracker.registerColor('cyan', function (r, g, b) {
    var thresholdGreen = 50,
      thresholdBlue = 70,
      dx = r - 0,
      dy = g - 255,
      dz = b - 255

    if (g - r >= thresholdGreen && b - r >= thresholdBlue) {
      return true
    }
    return dx * dx + dy * dy + dz * dz < 6400
  })

  tracking.ColorTracker.registerColor('magenta', function (r, g, b) {
    var threshold = 50,
      dx = r - 255,
      dy = g - 0,
      dz = b - 255

    if (r - g >= threshold && b - g >= threshold) {
      return true
    }
    return dx * dx + dy * dy + dz * dz < 19600
  })

  tracking.ColorTracker.registerColor('yellow', function (r, g, b) {
    var threshold = 50,
      dx = r - 255,
      dy = g - 255,
      dz = b - 0

    if (r - b >= threshold && g - b >= threshold) {
      return true
    }
    return dx * dx + dy * dy + dz * dz < 10000
  })

  // Caching neighbour i/j offset values.
  //=====================================
  var neighboursI = new Int32Array([-1, -1, 0, 1, 1, 1, 0, -1])
  var neighboursJ = new Int32Array([0, 1, 1, 1, 0, -1, -1, -1])
})()
;(function () {
  /**
   * ObjectTracker utility.
   * @constructor
   * @param {string|Array.<string|Array.<number>>} opt_classifiers Optional
   *     object classifiers to track.
   * @extends {tracking.Tracker}
   */
  tracking.ObjectTracker = function (opt_classifiers) {
    tracking.ObjectTracker.base(this, 'constructor')

    if (opt_classifiers) {
      if (!Array.isArray(opt_classifiers)) {
        opt_classifiers = [opt_classifiers]
      }

      if (Array.isArray(opt_classifiers)) {
        opt_classifiers.forEach(function (classifier, i) {
          if (typeof classifier === 'string') {
            opt_classifiers[i] = tracking.ViolaJones.classifiers[classifier]
          }
          if (!opt_classifiers[i]) {
            throw new Error(
              'Object classifier not valid, try `new tracking.ObjectTracker("face")`.'
            )
          }
        })
      }
    }

    this.setClassifiers(opt_classifiers)
  }

  tracking.inherits(tracking.ObjectTracker, tracking.Tracker)

  /**
   * Specifies the edges density of a block in order to decide whether to skip
   * it or not.
   * @default 0.2
   * @type {number}
   */
  tracking.ObjectTracker.prototype.edgesDensity = 0.2

  /**
   * Specifies the initial scale to start the feature block scaling.
   * @default 1.0
   * @type {number}
   */
  tracking.ObjectTracker.prototype.initialScale = 1.0

  /**
   * Specifies the scale factor to scale the feature block.
   * @default 1.25
   * @type {number}
   */
  tracking.ObjectTracker.prototype.scaleFactor = 1.25

  /**
   * Specifies the block step size.
   * @default 1.5
   * @type {number}
   */
  tracking.ObjectTracker.prototype.stepSize = 1.5

  /**
   * Gets the tracker HAAR classifiers.
   * @return {TypedArray.<number>}
   */
  tracking.ObjectTracker.prototype.getClassifiers = function () {
    return this.classifiers
  }

  /**
   * Gets the edges density value.
   * @return {number}
   */
  tracking.ObjectTracker.prototype.getEdgesDensity = function () {
    return this.edgesDensity
  }

  /**
   * Gets the initial scale to start the feature block scaling.
   * @return {number}
   */
  tracking.ObjectTracker.prototype.getInitialScale = function () {
    return this.initialScale
  }

  /**
   * Gets the scale factor to scale the feature block.
   * @return {number}
   */
  tracking.ObjectTracker.prototype.getScaleFactor = function () {
    return this.scaleFactor
  }

  /**
   * Gets the block step size.
   * @return {number}
   */
  tracking.ObjectTracker.prototype.getStepSize = function () {
    return this.stepSize
  }

  /**
   * Tracks the `Video` frames. This method is called for each video frame in
   * order to emit `track` event.
   * @param {Uint8ClampedArray} pixels The pixels data to track.
   * @param {number} width The pixels canvas width.
   * @param {number} height The pixels canvas height.
   */
  tracking.ObjectTracker.prototype.track = function (pixels, width, height) {
    var self = this
    var classifiers = this.getClassifiers()

    if (!classifiers) {
      throw new Error(
        'Object classifier not specified, try `new tracking.ObjectTracker("face")`.'
      )
    }

    var results = []

    classifiers.forEach(function (classifier) {
      results = results.concat(
        tracking.ViolaJones.detect(
          pixels,
          width,
          height,
          self.getInitialScale(),
          self.getScaleFactor(),
          self.getStepSize(),
          self.getEdgesDensity(),
          classifier
        )
      )
    })

    this.emit('track', {
      data: results,
    })
  }

  /**
   * Sets the tracker HAAR classifiers.
   * @param {TypedArray.<number>} classifiers
   */
  tracking.ObjectTracker.prototype.setClassifiers = function (classifiers) {
    this.classifiers = classifiers
  }

  /**
   * Sets the edges density.
   * @param {number} edgesDensity
   */
  tracking.ObjectTracker.prototype.setEdgesDensity = function (edgesDensity) {
    this.edgesDensity = edgesDensity
  }

  /**
   * Sets the initial scale to start the block scaling.
   * @param {number} initialScale
   */
  tracking.ObjectTracker.prototype.setInitialScale = function (initialScale) {
    this.initialScale = initialScale
  }

  /**
   * Sets the scale factor to scale the feature block.
   * @param {number} scaleFactor
   */
  tracking.ObjectTracker.prototype.setScaleFactor = function (scaleFactor) {
    this.scaleFactor = scaleFactor
  }

  /**
   * Sets the block step size.
   * @param {number} stepSize
   */
  tracking.ObjectTracker.prototype.setStepSize = function (stepSize) {
    this.stepSize = stepSize
  }
})()
;(function () {
  tracking.LandmarksTracker = function () {
    tracking.LandmarksTracker.base(this, 'constructor')
  }

  tracking.inherits(tracking.LandmarksTracker, tracking.ObjectTracker)

  tracking.LandmarksTracker.prototype.track = function (pixels, width, height) {
    var image = {
      data: pixels,
      width: width,
      height: height,
    }

    var classifier = tracking.ViolaJones.classifiers['face']

    var faces = tracking.ViolaJones.detect(
      pixels,
      width,
      height,
      this.getInitialScale(),
      this.getScaleFactor(),
      this.getStepSize(),
      this.getEdgesDensity(),
      classifier
    )

    var landmarks = tracking.LBF.align(pixels, width, height, faces)

    this.emit('track', {
      data: {
        faces: faces,
        landmarks: landmarks,
      },
    })
  }
})()
;(function () {
  tracking.LBF = {}

  /**
   * LBF Regressor utility.
   * @constructor
   */
  tracking.LBF.Regressor = function (maxNumStages) {
    this.maxNumStages = maxNumStages

    this.rfs = new Array(maxNumStages)
    this.models = new Array(maxNumStages)
    for (var i = 0; i < maxNumStages; i++) {
      this.rfs[i] = new tracking.LBF.RandomForest(i)
      this.models[i] = tracking.LBF.RegressorData[i].models
    }

    this.meanShape = tracking.LBF.LandmarksData
  }

  /**
   * Predicts the position of the landmarks based on the bounding box of the face.
   * @param {pixels} pixels The grayscale pixels in a linear array.
   * @param {number} width Width of the image.
   * @param {number} height Height of the image.
   * @param {object} boudingBox Bounding box of the face to be aligned.
   * @return {matrix} A matrix with each landmark position in a row [x,y].
   */
  tracking.LBF.Regressor.prototype.predict = function (
    pixels,
    width,
    height,
    boundingBox
  ) {
    var images = []
    var currentShapes = []
    var boundingBoxes = []

    var meanShapeClone = tracking.Matrix.clone(this.meanShape)

    images.push({
      data: pixels,
      width: width,
      height: height,
    })
    boundingBoxes.push(boundingBox)

    currentShapes.push(
      tracking.LBF.projectShapeToBoundingBox_(meanShapeClone, boundingBox)
    )

    for (var stage = 0; stage < this.maxNumStages; stage++) {
      var binaryFeatures = tracking.LBF.Regressor.deriveBinaryFeat(
        this.rfs[stage],
        images,
        currentShapes,
        boundingBoxes,
        meanShapeClone
      )
      this.applyGlobalPrediction(
        binaryFeatures,
        this.models[stage],
        currentShapes,
        boundingBoxes
      )
    }

    return currentShapes[0]
  }

  /**
   * Multiplies the binary features of the landmarks with the regression matrix
   * to obtain the displacement for each landmark. Then applies this displacement
   * into the landmarks shape.
   * @param {object} binaryFeatures The binary features for the landmarks.
   * @param {object} models The regressor models.
   * @param {matrix} currentShapes The landmarks shapes.
   * @param {array} boudingBoxes The bounding boxes of the faces.
   */
  tracking.LBF.Regressor.prototype.applyGlobalPrediction = function (
    binaryFeatures,
    models,
    currentShapes,
    boundingBoxes
  ) {
    var residual = currentShapes[0].length * 2

    var rotation = []
    var deltashape = new Array(residual / 2)
    for (var i = 0; i < residual / 2; i++) {
      deltashape[i] = [0.0, 0.0]
    }

    for (var i = 0; i < currentShapes.length; i++) {
      for (var j = 0; j < residual; j++) {
        var tmp = 0
        for (
          var lx = 0, idx = 0;
          (idx = binaryFeatures[i][lx].index) != -1;
          lx++
        ) {
          if (idx <= models[j].nr_feature) {
            tmp += models[j].data[idx - 1] * binaryFeatures[i][lx].value
          }
        }
        if (j < residual / 2) {
          deltashape[j][0] = tmp
        } else {
          deltashape[j - residual / 2][1] = tmp
        }
      }

      var res = tracking.LBF.similarityTransform_(
        tracking.LBF.unprojectShapeToBoundingBox_(
          currentShapes[i],
          boundingBoxes[i]
        ),
        this.meanShape
      )
      var rotation = tracking.Matrix.transpose(res[0])

      var s = tracking.LBF.unprojectShapeToBoundingBox_(
        currentShapes[i],
        boundingBoxes[i]
      )
      s = tracking.Matrix.add(s, deltashape)

      currentShapes[i] = tracking.LBF.projectShapeToBoundingBox_(
        s,
        boundingBoxes[i]
      )
    }
  }

  /**
   * Derives the binary features from the image for each landmark.
   * @param {object} forest The random forest to search for the best binary feature match.
   * @param {array} images The images with pixels in a grayscale linear array.
   * @param {array} currentShapes The current landmarks shape.
   * @param {array} boudingBoxes The bounding boxes of the faces.
   * @param {matrix} meanShape The mean shape of the current landmarks set.
   * @return {array} The binary features extracted from the image and matched with the
   *     training data.
   * @static
   */
  tracking.LBF.Regressor.deriveBinaryFeat = function (
    forest,
    images,
    currentShapes,
    boundingBoxes,
    meanShape
  ) {
    var binaryFeatures = new Array(images.length)
    for (var i = 0; i < images.length; i++) {
      var t = forest.maxNumTrees * forest.landmarkNum + 1
      binaryFeatures[i] = new Array(t)
      for (var j = 0; j < t; j++) {
        binaryFeatures[i][j] = {}
      }
    }

    var leafnodesPerTree = 1 << (forest.maxDepth - 1)

    for (var i = 0; i < images.length; i++) {
      var projectedShape = tracking.LBF.unprojectShapeToBoundingBox_(
        currentShapes[i],
        boundingBoxes[i]
      )
      var transform = tracking.LBF.similarityTransform_(
        projectedShape,
        meanShape
      )

      for (var j = 0; j < forest.landmarkNum; j++) {
        for (var k = 0; k < forest.maxNumTrees; k++) {
          var binaryCode = tracking.LBF.Regressor.getCodeFromTree(
            forest.rfs[j][k],
            images[i],
            currentShapes[i],
            boundingBoxes[i],
            transform[0],
            transform[1]
          )

          var index = j * forest.maxNumTrees + k
          binaryFeatures[i][index].index = leafnodesPerTree * index + binaryCode
          binaryFeatures[i][index].value = 1
        }
      }
      binaryFeatures[i][forest.landmarkNum * forest.maxNumTrees].index = -1
      binaryFeatures[i][forest.landmarkNum * forest.maxNumTrees].value = -1
    }
    return binaryFeatures
  }

  /**
   * Gets the binary code for a specific tree in a random forest. For each landmark,
   * the position from two pre-defined points are recovered from the training data
   * and then the intensity of the pixels corresponding to these points is extracted
   * from the image and used to traverse the trees in the random forest. At the end,
   * the ending nodes will be represented by 1, and the remaining nodes by 0.
   *
   * +--------------------------- Random Forest -----------------------------+
   * | Ø = Ending leaf                                                       |
   * |                                                                       |
   * |       O             O             O             O             O       |
   * |     /   \         /   \         /   \         /   \         /   \     |
   * |    O     O       O     O       O     O       O     O       O     O    |
   * |   / \   / \     / \   / \     / \   / \     / \   / \     / \   / \   |
   * |  Ø   O O   O   O   O Ø   O   O   Ø O   O   O   O Ø   O   O   O O   Ø  |
   * |  1   0 0   0   0   0 1   0   0   1 0   0   0   0 1   0   0   0 0   1  |
   * +-----------------------------------------------------------------------+
   * Final binary code for this landmark: 10000010010000100001
   *
   * @param {object} forest The tree to be analyzed.
   * @param {array} image The image with pixels in a grayscale linear array.
   * @param {matrix} shape The current landmarks shape.
   * @param {object} boudingBoxes The bounding box of the face.
   * @param {matrix} rotation The rotation matrix used to transform the projected landmarks
   *     into the mean shape.
   * @param {number} scale The scale factor used to transform the projected landmarks
   *     into the mean shape.
   * @return {number} The binary code extracted from the tree.
   * @static
   */
  tracking.LBF.Regressor.getCodeFromTree = function (
    tree,
    image,
    shape,
    boundingBox,
    rotation,
    scale
  ) {
    var current = 0
    var bincode = 0

    while (true) {
      var x1 =
        Math.cos(tree.nodes[current].feats[0]) *
        tree.nodes[current].feats[2] *
        tree.maxRadioRadius *
        boundingBox.width
      var y1 =
        Math.sin(tree.nodes[current].feats[0]) *
        tree.nodes[current].feats[2] *
        tree.maxRadioRadius *
        boundingBox.height
      var x2 =
        Math.cos(tree.nodes[current].feats[1]) *
        tree.nodes[current].feats[3] *
        tree.maxRadioRadius *
        boundingBox.width
      var y2 =
        Math.sin(tree.nodes[current].feats[1]) *
        tree.nodes[current].feats[3] *
        tree.maxRadioRadius *
        boundingBox.height

      var project_x1 = rotation[0][0] * x1 + rotation[0][1] * y1
      var project_y1 = rotation[1][0] * x1 + rotation[1][1] * y1

      var real_x1 = Math.floor(project_x1 + shape[tree.landmarkID][0])
      var real_y1 = Math.floor(project_y1 + shape[tree.landmarkID][1])
      real_x1 = Math.max(0.0, Math.min(real_x1, image.height - 1.0))
      real_y1 = Math.max(0.0, Math.min(real_y1, image.width - 1.0))

      var project_x2 = rotation[0][0] * x2 + rotation[0][1] * y2
      var project_y2 = rotation[1][0] * x2 + rotation[1][1] * y2

      var real_x2 = Math.floor(project_x2 + shape[tree.landmarkID][0])
      var real_y2 = Math.floor(project_y2 + shape[tree.landmarkID][1])
      real_x2 = Math.max(0.0, Math.min(real_x2, image.height - 1.0))
      real_y2 = Math.max(0.0, Math.min(real_y2, image.width - 1.0))
      var pdf =
        Math.floor(image.data[real_y1 * image.width + real_x1]) -
        Math.floor(image.data[real_y2 * image.width + real_x2])

      if (pdf < tree.nodes[current].thresh) {
        current = tree.nodes[current].cnodes[0]
      } else {
        current = tree.nodes[current].cnodes[1]
      }

      if (tree.nodes[current].is_leafnode == 1) {
        bincode = 1
        for (var i = 0; i < tree.leafnodes.length; i++) {
          if (tree.leafnodes[i] == current) {
            return bincode
          }
          bincode++
        }
        return bincode
      }
    }

    return bincode
  }
})()
;(function () {
  /**
   * Face Alignment via Regressing Local Binary Features (LBF)
   * This approach has two components: a set of local binary features and
   * a locality principle for learning those features.
   * The locality principle is used to guide the learning of a set of highly
   * discriminative local binary features for each landmark independently.
   * The obtained local binary features are used to learn a linear regression
   * that later will be used to guide the landmarks in the alignment phase.
   *
   * @authors: VoxarLabs Team (http://cin.ufpe.br/~voxarlabs)
   *           Lucas Figueiredo <lsf@cin.ufpe.br>, Thiago Menezes <tmc2@cin.ufpe.br>,
   *           Thiago Domingues <tald@cin.ufpe.br>, Rafael Roberto <rar3@cin.ufpe.br>,
   *           Thulio Araujo <tlsa@cin.ufpe.br>, Joao Victor <jvfl@cin.ufpe.br>,
   *           Tomer Simis <tls@cin.ufpe.br>)
   */

  /**
   * Holds the maximum number of stages that will be used in the alignment algorithm.
   * Each stage contains a different set of random forests and retrieves the binary
   * code from a more "specialized" (i.e. smaller) region around the landmarks.
   * @type {number}
   * @static
   */
  tracking.LBF.maxNumStages = 4

  /**
   * Holds the regressor that will be responsible for extracting the local features from
   * the image and guide the landmarks using the training data.
   * @type {object}
   * @protected
   * @static
   */
  tracking.LBF.regressor_ = null

  /**
   * Generates a set of landmarks for a set of faces
   * @param {pixels} pixels The pixels in a linear [r,g,b,a,...] array.
   * @param {number} width The image width.
   * @param {number} height The image height.
   * @param {array} faces The list of faces detected in the image
   * @return {array} The aligned landmarks, each set of landmarks corresponding
   *     to a specific face.
   * @static
   */
  tracking.LBF.align = function (pixels, width, height, faces) {
    if (tracking.LBF.regressor_ == null) {
      tracking.LBF.regressor_ = new tracking.LBF.Regressor(
        tracking.LBF.maxNumStages
      )
    }

    pixels = tracking.Image.grayscale(pixels, width, height, false)

    pixels = tracking.Image.equalizeHist(pixels, width, height)

    var shapes = new Array(faces.length)

    for (var i in faces) {
      faces[i].height = faces[i].width

      var boundingBox = {}
      boundingBox.startX = faces[i].x
      boundingBox.startY = faces[i].y
      boundingBox.width = faces[i].width
      boundingBox.height = faces[i].height

      shapes[i] = tracking.LBF.regressor_.predict(
        pixels,
        width,
        height,
        boundingBox
      )
    }

    return shapes
  }

  /**
   * Unprojects the landmarks shape from the bounding box.
   * @param {matrix} shape The landmarks shape.
   * @param {matrix} boudingBox The bounding box.
   * @return {matrix} The landmarks shape projected into the bounding box.
   * @static
   * @protected
   */
  tracking.LBF.unprojectShapeToBoundingBox_ = function (shape, boundingBox) {
    var temp = new Array(shape.length)
    for (var i = 0; i < shape.length; i++) {
      temp[i] = [
        (shape[i][0] - boundingBox.startX) / boundingBox.width,
        (shape[i][1] - boundingBox.startY) / boundingBox.height,
      ]
    }
    return temp
  }

  /**
   * Projects the landmarks shape into the bounding box. The landmarks shape has
   * normalized coordinates, so it is necessary to map these coordinates into
   * the bounding box coordinates.
   * @param {matrix} shape The landmarks shape.
   * @param {matrix} boudingBox The bounding box.
   * @return {matrix} The landmarks shape.
   * @static
   * @protected
   */
  tracking.LBF.projectShapeToBoundingBox_ = function (shape, boundingBox) {
    var temp = new Array(shape.length)
    for (var i = 0; i < shape.length; i++) {
      temp[i] = [
        shape[i][0] * boundingBox.width + boundingBox.startX,
        shape[i][1] * boundingBox.height + boundingBox.startY,
      ]
    }
    return temp
  }

  /**
   * Calculates the rotation and scale necessary to transform shape1 into shape2.
   * @param {matrix} shape1 The shape to be transformed.
   * @param {matrix} shape2 The shape to be transformed in.
   * @return {[matrix, scalar]} The rotation matrix and scale that applied to shape1
   *     results in shape2.
   * @static
   * @protected
   */
  tracking.LBF.similarityTransform_ = function (shape1, shape2) {
    var center1 = [0, 0]
    var center2 = [0, 0]
    for (var i = 0; i < shape1.length; i++) {
      center1[0] += shape1[i][0]
      center1[1] += shape1[i][1]
      center2[0] += shape2[i][0]
      center2[1] += shape2[i][1]
    }
    center1[0] /= shape1.length
    center1[1] /= shape1.length
    center2[0] /= shape2.length
    center2[1] /= shape2.length

    var temp1 = tracking.Matrix.clone(shape1)
    var temp2 = tracking.Matrix.clone(shape2)
    for (var i = 0; i < shape1.length; i++) {
      temp1[i][0] -= center1[0]
      temp1[i][1] -= center1[1]
      temp2[i][0] -= center2[0]
      temp2[i][1] -= center2[1]
    }

    var covariance1, covariance2
    var mean1, mean2

    var t = tracking.Matrix.calcCovarMatrix(temp1)
    covariance1 = t[0]
    mean1 = t[1]

    t = tracking.Matrix.calcCovarMatrix(temp2)
    covariance2 = t[0]
    mean2 = t[1]

    var s1 = Math.sqrt(tracking.Matrix.norm(covariance1))
    var s2 = Math.sqrt(tracking.Matrix.norm(covariance2))

    var scale = s1 / s2
    temp1 = tracking.Matrix.mulScalar(1.0 / s1, temp1)
    temp2 = tracking.Matrix.mulScalar(1.0 / s2, temp2)

    var num = 0,
      den = 0
    for (var i = 0; i < shape1.length; i++) {
      num = num + temp1[i][1] * temp2[i][0] - temp1[i][0] * temp2[i][1]
      den = den + temp1[i][0] * temp2[i][0] + temp1[i][1] * temp2[i][1]
    }

    var norm = Math.sqrt(num * num + den * den)
    var sin_theta = num / norm
    var cos_theta = den / norm
    var rotation = [
      [cos_theta, -sin_theta],
      [sin_theta, cos_theta],
    ]

    return [rotation, scale]
  }

  /**
   * LBF Random Forest data structure.
   * @static
   * @constructor
   */
  tracking.LBF.RandomForest = function (forestIndex) {
    this.maxNumTrees = tracking.LBF.RegressorData[forestIndex].max_numtrees
    this.landmarkNum = tracking.LBF.RegressorData[forestIndex].num_landmark
    this.maxDepth = tracking.LBF.RegressorData[forestIndex].max_depth
    this.stages = tracking.LBF.RegressorData[forestIndex].stages

    this.rfs = new Array(this.landmarkNum)
    for (var i = 0; i < this.landmarkNum; i++) {
      this.rfs[i] = new Array(this.maxNumTrees)
      for (var j = 0; j < this.maxNumTrees; j++) {
        this.rfs[i][j] = new tracking.LBF.Tree(forestIndex, i, j)
      }
    }
  }

  /**
   * LBF Tree data structure.
   * @static
   * @constructor
   */
  tracking.LBF.Tree = function (forestIndex, landmarkIndex, treeIndex) {
    var data =
      tracking.LBF.RegressorData[forestIndex].landmarks[landmarkIndex][
        treeIndex
      ]
    this.maxDepth = data.max_depth
    this.maxNumNodes = data.max_numnodes
    this.nodes = data.nodes
    this.landmarkID = data.landmark_id
    this.numLeafnodes = data.num_leafnodes
    this.numNodes = data.num_nodes
    this.maxNumFeats = data.max_numfeats
    this.maxRadioRadius = data.max_radio_radius
    this.leafnodes = data.id_leafnodes
  }
})()
